#pragma once

#include <algorithm>
#include <functional>
#include <memory>
#include <vector>
#include <data/channel_buffer.h>
#include <data/data_dispatcher.h>
#include <data/data_visitor_base.h>

class NullType {};

struct VisitorConfig {
    VisitorConfig(uint64_t id, uint32_t size)
        : channel_id(id), queue_size(size) {}
    uint64_t channel_id;
    uint64_t queue_size;
};

template <typename T>
using BufferType = CacheBuffer<std::shared_ptr<T>>;


template <typename M0>
class DataVisitor : public DataVisitorBase {
 public:
  explicit DataVisitor(const VisitorConfig& configs)
      : buffer_(configs.channel_id, new BufferType<M0>(configs.queue_size)) {
    DataDispatcher<M0>::Instance()->AddBuffer(buffer_);
    data_notifier_->AddNotifier(buffer_.channel_id(), notifier_);
  }

  DataVisitor(uint64_t channel_id, uint32_t queue_size)
      : buffer_(channel_id, new BufferType<M0>(queue_size)) {
    DataDispatcher<M0>::Instance()->AddBuffer(buffer_);
    data_notifier_->AddNotifier(buffer_.channel_id(), notifier_);
  }

  bool TryFetch(std::shared_ptr<M0>& m0) {  // NOLINT
    if (buffer_.Fetch(&next_msg_index_, m0)) {
      next_msg_index_++;
      return true;
    }
    return false;
  }

 private:
  ChannelBuffer<M0> buffer_;
};
